package self.sn.chat.core;

import self.sn.chat.ui.MyFrame;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class NIOServer {

    private final ByteBuffer readBuffer = ByteBuffer.allocateDirect(10240);
    private final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(10240);
    private Selector selector = null;
    private final List<SocketChannel> CHANNELS = new LinkedList<>();

    public void init() throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(8080));
        this.selector = Selector.open();;
        // 绑定channel的accept
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public NIOServer() throws Exception {
        init();
        select();
    }

    /**
     * 客户端事件驱动处理
     * @throws Exception
     */
    private void select() throws Exception{
        while(selector.select()>0){
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                if(selectionKey.isAcceptable()){
                    ServerSocketChannel server = (ServerSocketChannel)selectionKey.channel();
                    // 新注册channel
                    SocketChannel socketChannel  = server.accept();
                    if(socketChannel==null) continue;

                    System.out.println("客户端连接:" + socketChannel);
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);

                    CHANNELS.add(socketChannel);
                    acceptNotify();
                }
                if(selectionKey.isReadable()){
                    SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                    try {
                        readBuffer.clear();
                        socketChannel.read(readBuffer);
                        readBuffer.flip();

                        // op + data
                        String receiveData = socketChannel.getRemoteAddress().toString().substring(1) + "：\n " + StandardCharsets.UTF_8.decode(readBuffer);
                        byte[] bytes = receiveData.getBytes(StandardCharsets.UTF_8);

                        //写回给其他客户端
                        for (SocketChannel channel : CHANNELS) {
                            writeBuffer.put((byte) MyFrame.Op.MSG.ordinal());
                            if (channel.equals(socketChannel)) {
                                byte[] self = " (自己) ".getBytes(StandardCharsets.UTF_8);
                                byte[] res = new byte[bytes.length + self.length];
                                System.arraycopy(self, 0, res, 0, self.length);
                                System.arraycopy(bytes, 0, res, self.length, bytes.length);
                                write(channel, res);
                            } else {
                                write(channel, bytes);
                            }
                        }
                    } catch (IOException e) {
                        System.out.println(socketChannel.getRemoteAddress() + " 断开链接..");
                        selectionKey.cancel();
                        CHANNELS.remove(socketChannel);
                        acceptNotify();
                    }
                }
            }
        }
    }

    /**
     * 连接通知
     */
    public void acceptNotify() throws IOException {
        StringBuilder ipList = new StringBuilder(" 在线人员:\n");
        for (SocketChannel channel : CHANNELS) {
            try {
                ipList.append(channel.getRemoteAddress().toString().substring(1)).append("\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        byte[] bytes = ipList.toString().getBytes(StandardCharsets.UTF_8);
        for (SocketChannel channel : CHANNELS) {
            writeBuffer.put((byte) MyFrame.Op.ONLINE.ordinal());
            write(channel, bytes);
        }
    }

    /**
     * 给客户端写数据
     * @param socketChannel 通道
     * @param bytes 数据
     * @throws IOException
     */
    public void write(SocketChannel socketChannel,byte[] bytes) throws IOException {
        writeBuffer.put(bytes);
        while(writeBuffer.hasRemaining()){
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
        }
        writeBuffer.clear();
    }

}
